﻿/// @file kaslist.c
#include "stdafx.h"
#include "sbcs.h"
#include "sbctn.h"

//////////////////////////////////////////////////////////////////////////
// 비동기 리스트

// 리스트
struct kAsyncList
{
	kMutex*				mutex;
	kCond*				cond;

	kPtrList			list;

	kint				waits;
};

/// 비동기큐 만들기.
/// @author KSH
/// @date 2013-12-22
/// @return 실패하면 null을 반환.
kAsyncList* k_aslq_new(void)
{
	kAsyncList* self = k_new_0(kAsyncList);

	self->mutex = k_mutex_new();
	self->cond = NULL;

	k_plist_init(&self->list);

	self->waits = 0;

	return self;
}

/// 비동기큐 제거.
/// @author KSH
/// @date 2013-12-22
/// @param self 자기 자신 참조 포인터.
/// @return 성공하면 참을 실패하면 거짓을 반환한다.
kcham k_aslq_delete(kAsyncList* self)
{
	k_return_value_if_fail(self != NULL, FALSE);
#if 0
	k_return_value_if_fail(self->waits == 0, FALSE);
#else
	if (self->waits > 0)
		k_sleep(100);
#endif

	k_mutex_delete(self->mutex);

	if (self->cond)
		k_cond_delete(self->cond);

	k_plist_disp(&self->list);

	k_delete(self);

	return TRUE;
}

/// 비동기큐 내부 데이터 잠금.
/// @author KSH
/// @date 2013-12-22
/// @param self 자기 자신 참조 포인터.
void k_aslq_enter(kAsyncList* self)
{
	k_return_if_fail(self != NULL);

	k_mutex_enter(self->mutex);
}

/// 비동기큐 내부 데이터 풀기.
/// @author KSH
/// @date 2013-12-22
/// @param self 자기 자신 참조 포인터.
void k_aslq_leave(kAsyncList* self)
{
	k_return_if_fail(self != NULL);

	k_mutex_leave(self->mutex);
}

// 넣기
static void _k_aslq_push_internal(kAsyncList* self, kpointer data)
{
	k_plist_prepend(&self->list, data);

	if (self->waits > 0)
		k_cond_signal(self->cond);
}

/// 비동기큐 잠그지 않고 데이터 넣기.
/// @author KSH
/// @date 2013-12-22
/// @param self 자기 자신 참조 포인터.
/// @param data 자료.
/// @return 성공하면 참을 실패하면 거짓을 반환한다.
kcham k_aslq_push_nolock(kAsyncList* self, kpointer data)
{
	k_return_value_if_fail(self != NULL, FALSE);
	//k_return_value_if_fail(data!=NULL, FALSE);

	_k_aslq_push_internal(self, data);

	return TRUE;
}

/// 비동기큐 데이터 넣기.
/// @date 2013-12-22
/// @param self 자기 자신 참조 포인터.
/// @param data 자료.
/// @return 성공하면 참을 실패하면 거짓을 반환한다.
kcham k_aslq_push(kAsyncList* self, kpointer data)
{
	k_return_value_if_fail(self != NULL, FALSE);
	//k_return_value_if_fail(data!=NULL, FALSE);

	k_mutex_enter(self->mutex);
	_k_aslq_push_internal(self, data);
	k_mutex_leave(self->mutex);

	return TRUE;
}

// 뽑기
static kpointer _k_aslq_pop_internal(kAsyncList* self, kint milliseconds)
{
	kpointer ret;

	if (!k_plist_peek_last(&self->list))
	{
		if (!self->cond)
			self->cond = k_cond_new();

		if (milliseconds < 0)
		{
			self->waits++;

			while (!k_plist_peek_last(&self->list))
				k_cond_wait(self->cond, self->mutex);

			self->waits--;
		}
		else
		{
			self->waits++;

			while (!k_plist_peek_last(&self->list))
			{
				if (!k_cond_wait_for(self->cond, self->mutex, milliseconds))
					break;
			}

			self->waits--;

			if (!k_plist_peek_last(&self->list))
				return NULL;
		}
	}
	else
	{
#if _SB_DEBUG_
		ret = FALSE;
#endif
	}

	ret = k_plist_last(&self->list);
	k_plist_remove_last(&self->list);

	return ret;
}

/// 비동기큐 잠그지 않고 데이터 뽑기.
/// @date 2013-12-22
/// @param self		    자기 자신 참조 포인터.
/// @param milliseconds 밀리초.
/// @return 포인터 값.
kpointer k_aslq_pop_nolock(kAsyncList* self, kint milliseconds)
{
	k_return_value_if_fail(self != NULL, NULL);

	return _k_aslq_pop_internal(self, milliseconds);
}

/// 비동기큐 데이터 뽑기.
/// @date 2013-12-22
/// @param self 자기 자신 참조 포인터.
/// @return 포인터 값.
kpointer k_aslq_pop(kAsyncList* self)
{
	kpointer ret;

	k_return_value_if_fail(self != NULL, NULL);

	k_mutex_enter(self->mutex);
	ret = _k_aslq_pop_internal(self, -1);
	k_mutex_leave(self->mutex);

	return ret;
}

/// 비동기큐 데이터 뽑기. 지정한 시간 만큼 기다린다.
/// @date 2013-12-22
/// @param self		    자기 자신 참조 포인터.
/// @param milliseconds 기다릴 시간(밀리초).
/// @return 포인터 값.
kpointer k_aslq_pop_wait(kAsyncList* self, kint milliseconds)
{
	kpointer ret;

	k_return_value_if_fail(self != NULL, NULL);

	k_mutex_enter(self->mutex);
	ret = _k_aslq_pop_internal(self, milliseconds);
	k_mutex_leave(self->mutex);

	return ret;
}

/// 비동기큐 잠그지 않고 데이터 뽑기. 지정한 시간 만큼 기다린다.
/// @date 2013-12-22
/// @param self		    자기 자신 참조 포인터.
/// @param milliseconds 밀리초.
/// @return 포인터 값.
kpointer k_aslq_pop_wait_nolock(kAsyncList* self, kint milliseconds)
{
	k_return_value_if_fail(self != NULL, NULL);

	return _k_aslq_pop_internal(self, milliseconds);
}

// 트라이
static kpointer _k_aslq_try_internal(kAsyncList* self)
{
	kpointer ret;

	if (!k_plist_peek_last(&self->list))
		ret = NULL;
	else
	{
		ret = k_plist_last(&self->list);
		k_plist_remove_last(&self->list);
	}

	return ret;
}

/// 비동기큐 잠그지 않고 데이터 확인.
/// @date 2013-12-22
/// @param self 자기 자신 참조 포인터.
/// @return 포인터 값.
kpointer k_aslq_try_nolock(kAsyncList* self)
{
	k_return_value_if_fail(self != NULL, NULL);

	return _k_aslq_try_internal(self);
}

/// 비동기큐 데이터 확인.
/// @date 2013-12-22
/// @param self 자기 자신 참조 포인터.
/// @return 포인터 값.
kpointer k_aslq_try(kAsyncList* self)
{
	kpointer ret;

	k_return_value_if_fail(self != NULL, NULL);

	k_mutex_enter(self->mutex);
	ret = _k_aslq_try_internal(self);
	k_mutex_leave(self->mutex);

	return ret;
}

/// 비동기큐 데이터 개수.
/// @date 2013-12-22
/// @param self 자기 자신 참조 포인터.
/// @return 데이터 개수.
kint k_aslq_count(kAsyncList* self)
{
	kint ret;

	k_return_value_if_fail(self != NULL, -1);

	k_mutex_enter(self->mutex);
	ret = (kint)k_plist_count(&self->list);
	k_mutex_leave(self->mutex);

	return ret;
}

/// 비동기큐 잠그지 않고 데이터 개수 얻기.
/// @date 2013-12-22
/// @param self 자기 자신 참조 포인터.
/// @return 데이터 개수.
kint k_aslq_count_nolock(kAsyncList* self)
{
	k_return_value_if_fail(self != NULL, -1);

	return (kint)k_plist_count(&self->list);
}

/// 비동기큐 데이터 길이.
/// @date 2013-12-22
/// @param self 자기 자신 참조 포인터.
/// @return 정수 값.
kint k_aslq_length(kAsyncList* self)
{
	kint ret;

	k_return_value_if_fail(self != NULL, -1);

	k_mutex_enter(self->mutex);
	ret = (kint)k_plist_count(&self->list) - self->waits;
	k_mutex_leave(self->mutex);

	return ret;
}

/// 비동기큐 잠그지 않고 데이터 길이.
/// @date 2013-12-22
/// @param self 자기 자신 참조 포인터.
/// @return 정수 값.
kint k_aslq_length_nolock(kAsyncList* self)
{
	k_return_value_if_fail(self != NULL, -1);

	return (kint)k_plist_count(&self->list) - self->waits;
}

/// 비동기큐 뮤텍스 얻기.
/// @date 2013-12-22
/// @param self 자기 자신 참조 포인터.
/// @return 실패하면 null을 반환. 그렇지 아니면 kMutex*.
kMutex* k_aslq_get_mutex(kAsyncList* self)
{
	k_return_value_if_fail(self != NULL, NULL);

	return self->mutex;
}

/// 비동기큐 컨티션 얻기.
/// @date 2013-12-22
/// @param self 자기 자신 참조 포인터.
/// @return 실패하면 null을 반환. 그렇지 아니면 kCond*.
kCond* k_aslq_get_cond(kAsyncList* self)
{
	k_return_value_if_fail(self != NULL, NULL);

	return self->cond;
}

